package com.ktwlsoft.fundmanage.controller;

import java.math.BigDecimal;
import java.util.*;

import javax.validation.Valid;

import com.ktwlsoft.fundmanage.domain.business.FundInService;
import com.ktwlsoft.fundmanage.domain.business.FundOutService;
import com.ktwlsoft.fundmanage.domain.entity.FundInRecord;
import com.ktwlsoft.fundmanage.domain.entity.FundSuperviseAccept;
import com.ktwlsoft.fundmanage.domain.entity.TransferApply;
import com.ktwlsoft.fundmanage.fundFlow.service.FundOutFlowService;
import com.ktwlsoft.fundmanage.payload.BaseResult;
import com.ktwlsoft.fundmanage.payload.ResultType;
import com.ktwlsoft.fundmanage.payload.exceptions.BusinessError;
import com.ktwlsoft.fundmanage.payload.fundIn.TransferCancelRequest;
import com.ktwlsoft.fundmanage.payload.fundOut.FundOutCheckRequest;
import com.ktwlsoft.fundmanage.payload.fundOut.FundOutOrCancelRequest;
import com.ktwlsoft.fundmanage.payload.fundOut.RemodifyFundOut;
import com.ktwlsoft.fundmanage.payload.fundOut.TransactBatchRequest;
import com.ktwlsoft.fundmanage.payload.fundOut.TransactRequest;
import com.ktwlsoft.fundmanage.payload.fundOut.TransferApprovalBatchRequest;
import com.ktwlsoft.fundmanage.utils.KeyWorkerUtil;
import com.ktwlsoft.fundmanage.utils.UserUtils;

import io.swagger.annotations.ApiModelProperty;
import org.activiti.engine.task.Task;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

/**
 * 出账及退款相关操作, 作者：兰坚
 *
 * @author LanJian
 */
@Api(description = "出账及退款, 作者：兰坚")
@RequestMapping("/flowOut")
@RestController
public class FlowOutController {

    private final FundOutFlowService fundOutFlowService;

    private final FundOutService fundOutService;

    private final FundInService fundInService;

    public FlowOutController(FundOutFlowService fundOutFlowService, FundOutService fundOutService,
                             FundInService fundInService) {
        this.fundOutFlowService = fundOutFlowService;
        this.fundOutService = fundOutService;
        this.fundInService = fundInService;
    }

    @ApiOperation("批量开具划款单，注意返回的是未能正常开具的划款单请求集合")
    @PostMapping("/chooseTransferBatch")
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<?> chooseTransferBatch(@RequestBody TransactBatchRequest request) {
        // 检查此流程是否有完整的受理人设置
        if (!fundOutFlowService.checkOperator()) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(BaseResult.body("审核流程未配置处理人！", ResultType.BUSINESS_ERROR));
        }
        //未能正常开具划款单的结果集合
        List<BaseResult> results = new ArrayList<>();
        //批量开具的划款单必须拥有同样的流水号
        long flowCode = KeyWorkerUtil.nextId();
        try {
            BigDecimal amountLeft = BigDecimal.ZERO;
            for (TransactRequest transact : request.getRequests()) {
                transact.setApprovalFlow(flowCode);
                startTransact(transact);
                amountLeft = amountLeft.add(transact.getAmount());
            }
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(results);
        }
        return ResponseEntity.ok(results);
    }

    public void startTransact(TransactRequest request) {
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("transferType", request.getTransactType().toString());
        if (request.getTransactType() == TransferApply.TransferType.NORMAL) {
            FundSuperviseAccept fundSuperviseAccept = fundInService.findBusinessByTsCode(request.getTsCode());
            if (!fundSuperviseAccept.isTransferState()) {
                throw new BusinessError(request.getTsCode() + ": 房子没有过户");
            }
            if (fundOutService.doTransferApplyExist(request.getTsCode(), request.getFCode())) {
                throw new BusinessError("开具划款单: 划款单重复！");
            }

        } else if (request.getTransactType() == TransferApply.TransferType.REFUND) {
            if (fundOutService.doTransferApplyExist(request.getTsCode(), request.getFCode())) {
                throw new BusinessError("开具划款单: 划款单重复！");
            }
        } else if (request.getTransactType() == TransferApply.TransferType.THIRD) {

        }
        FundInRecord fundInRecord = fundInService.findFundInResult(request.getFCode());
        // Check if more than fundIn amount
        if (fundInRecord.getHasTransfer().add(request.getAmount()).compareTo(fundInRecord.getAmount()) > 0) {
            throw new BusinessError(request.getTsCode() + ":划款过多！");
        }
        request.setTransId(0);
        variables.put("transferApply", request);
        fundOutFlowService.startProcess("KTWL-FUND-MANAGE-FUND-OUT", variables);
    }

    @ApiOperation("划款单批量审核")
    @PutMapping("/transferApprovalBatch")
    public ResponseEntity<?> transferApprovalBatch(@RequestBody TransferApprovalBatchRequest request) {
        List<String> roles = UserUtils.getRoles();
        // 审核入口
        BaseResult result = fundOutFlowService.completeTaskBatch(request, roles);
        if (result.getResultType() == ResultType.BUSINESS_ERROR) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(result);
        } else {
            return ResponseEntity.ok(result);
        }
    }

    @ApiOperation("划款单划款")
    @PostMapping("transferFundOut")
    public ResponseEntity<?> transferFundOut(@RequestBody FundOutOrCancelRequest fundOutOrCancelRequest) {
        try {
            List<String> roles = UserUtils.getRoles();
            Task task = fundOutFlowService.getTask("Fund Out Or Cancel", fundOutOrCancelRequest.getInstanceId());
            Map<String, Object> variables = new HashMap<>();
            variables.put("fundOut", true);
            variables.put("fundOutOrCancelRequest", fundOutOrCancelRequest);
            variables.put("transId", fundOutOrCancelRequest.getTransferId());
            fundOutFlowService.completeTask(task.getId(), variables);
            return ResponseEntity.ok(BaseResult.ok("O"));
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(BaseResult.body(null,
                    ResultType.BUSINESS_ERROR).withMessage(ex.getMessage()));
        }
    }

    @ApiOperation("划款单撤销")
    @PostMapping("transferCancel")
    public ResponseEntity<?> transferCancel(@RequestBody TransferCancelRequest request) {
        try {
            //批量开具的划款单要批量的撤回！
            List<FundOutOrCancelRequest> requests = fundOutService.generateTransferRequest(request);
            for (FundOutOrCancelRequest f : requests) {
                Task task = fundOutFlowService.getTask("Fund Out Or Cancel", f.getInstanceId());
                Map<String, Object> variables = new HashMap<>();
                variables.put("fundOut", false);
                variables.put("fundOutOrCancelRequest", f);
                variables.put("transId", f.getTransferId());
                fundOutFlowService.completeTask(task.getId(), variables);
            }
            if (requests.size() <= 0) {
                return ResponseEntity.status(HttpStatus.CONFLICT).body("未找到任何划款单！");
            }
            return ResponseEntity.ok(BaseResult.ok("OK"));
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(BaseResult.body(null,
                    ResultType.BUSINESS_ERROR).withMessage(ex.getMessage()));
        }
    }

//    @ApiOperation("强制退回划款单申请")
//    @PutMapping("/{transferId}/forceCancelTransferApply")
//    public ResponseEntity<?> forceCancelTransferApply(@PathVariable String transId) {
//        long transferId = Long.parseLong(transId);
//        fundOutFlowService.forceCancelTransApply(transferId);transferApprovalBatch
//        return ResponseEntity.ok(BaseResult.ok("OK"));
//    }

    @ApiOperation("重新编辑并重新生成划款单")
    @PostMapping("/remodifyTransferApply")
    public ResponseEntity<?> remodifyTransferApply(@Valid @RequestBody TransactRequest request, BindingResult result) {
        try {
            if (result.hasErrors()) {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("请求数据有误");
            }
            if (request.getTransId() <= 0) {
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("划款单Id不能为空!");
            }
            Task task = fundOutFlowService.getTask("Remodify Transfer Apply", request.getInstanceId());
            Map<String, Object> variables = new HashMap<>();
            variables.put("transferApply", request);
            fundOutFlowService.completeTask(task.getId(), variables);
            return ResponseEntity.ok(BaseResult.ok("Edit transfer apply successful!"));
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(BaseResult.body(null,
                    ResultType.BUSINESS_ERROR).withMessage(ex.getMessage()));
        }
    }

    @ApiOperation("出账对账")
    @PutMapping("/fundOutCheck")
    public ResponseEntity<?> fundOutCheck(@RequestBody FundOutCheckRequest request) {
        try {
            Task task = fundOutFlowService.getTask("Account Check", request.getInstanceId());
            Map<String, Object> variables = new HashMap<>();
            //对账通过还是撤销，这里只是对activiti设置过程参数
            variables.put("outApproval", request.getOutApproval().toString());
            variables.put("fundOutApprovalResult", request);
            //以下if语句中再去判断是通过还是撤销, 正常流程执行到此就结束了
            if (fundOutService.fundOutCheck(request.getFCode(), request.getOutApproval(), request.getExpenditureTime(), request.getTsCode())) {
                fundOutFlowService.completeTask(task.getId(), variables);
                return ResponseEntity.ok(BaseResult.ok("任务完成"));
            } else {
                return ResponseEntity.status(HttpStatus.CONFLICT)
                        .body(BaseResult.ok("FundOut check failed, influenced rows not more than 0"));
            }
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(BaseResult.body(null,
                    ResultType.BUSINESS_ERROR).withMessage(ex.getMessage()));
        }
    }

    @ApiOperation("修改出账数据")
    @PutMapping("/modifyFundOut")
    public ResponseEntity<?> modifyFundOutData(@RequestBody RemodifyFundOut request) {
        try {
            Task task = fundOutFlowService.getTask("Remodify Fund Out", request.getInstanceId());
            Map<String, Object> variables = new HashMap<>();

            //撤销出账和划款单
            if (request.isCancelOrModify()) {
                // 撤销
                if (request.getFCode() == 0) {
                    throw new BusinessError("撤销出账数据，FCode不能为空！");
                }
                variables.put("cancelOrModify", "CANCEL");
                //为了迎合CancelApply, 故意生成FundOutOrCancelRequest对象和transId
                variables.put("transId", request.getTransferId());
                FundOutOrCancelRequest fundOutOrCancelRequest = new FundOutOrCancelRequest();
                fundOutOrCancelRequest.setBankFlowCode(request.getBankFlowCode());
                fundOutOrCancelRequest.setCreatedAt(new Date());
                fundOutOrCancelRequest.setRemark(request.getRemark());
                fundOutOrCancelRequest.setTransferId(request.getTransferId());
                fundOutOrCancelRequest.setType(request.getFundType());
                fundOutOrCancelRequest.setTsCode(request.getTsCode());
                variables.put("fundOutOrCancelRequest", fundOutOrCancelRequest);
                if (!fundOutService.cancelFundOut(request.getFCode())) {
                    throw new BusinessError("撤销出账失败！此划款单下可能没有出账数据！");
                }
            } else {
                //修改出账数据
                variables.put("cancelOrModify", "MODIFY");
                //作废旧出账数据，生成新的出账数据
                BaseResult result = fundOutService.cancelOldAndGenerateNew(request.getFCode(), request.getTransferId(),
                        request.getInstanceId(), request.getTransferType(), request.getCreatedAt(), request.getRemark(),
                        request.getBankFlowCode(), request.getFundType());
                if (result.getResultType() != ResultType.BUSINESS_SUCCESS) {
                    return ResponseEntity.status(HttpStatus.CONFLICT).body(result.getMessage());
                }
            }

            fundOutFlowService.completeTask(task.getId(), variables);
            return ResponseEntity.ok(BaseResult.ok("Fund out data modified!"));
        } catch (Exception ex) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(BaseResult.body(null,
                    ResultType.BUSINESS_ERROR).withMessage(ex.getMessage()));
        }
    }

    @GetMapping("/getFundOut")
    @ApiOperation("查找出账数据")
    public ResponseEntity<?> getFundOut(@ApiParam("出账主键") @RequestParam(required = true) long fCode) {
        return ResponseEntity.ok(BaseResult.ok(fundOutService.findFundOut(fCode)));
    }

    @ApiOperation("设置其他收款人下-更新划款单")
    @PutMapping("/setOtherReceiverForRefund")
    public ResponseEntity<?> setOtherReceiverForRefund(@RequestBody TransferApply request) {
        fundOutService.updateTransferApply(request);
        return ResponseEntity.ok(BaseResult.ok("Set completed!"));
    }

    @ApiOperation("查找所有划款单退回")
    @GetMapping("/findTransferApplyCancel")
    public ResponseEntity<?> findFundInCancel() {
        return ResponseEntity.ok(fundOutService.findTransferApplyCancel());
    }

    @ApiModelProperty("正常是否能退回？")
    @GetMapping("/checkWhetherCancel")
    public ResponseEntity<?> checkWhetherCancel(@RequestParam long approvalFlow, @RequestParam long transId) {
        return ResponseEntity.ok(BaseResult.ok(fundOutService.canTransferCancel(approvalFlow, transId)));
    }
}
